/*
 * net_session.h
 * 	这头文件只负责定义 TCP 网络模块中的 “会话” 这一抽象的概念
 * 	定义了 “会话” 的内部结构以及其 创建、销毁等方法
 * 	使用 “会话” 对象的内容基本上位于 net_manager.c文件
 */

#ifndef NET_SESSION_H_
#define NET_SESSION_H_

#include <pthread.h>
#include <sys/types.h>
#include <cm/struct/cm_fifo.h>
#include <cm/struct/list.h>
#include <cm/net/net_manager.h>
#include "net_constant.h"

enum session_type {
	SESSION_CLIENT, SESSION_SERVER
};

enum session_state {
	SESSION_NONE,		/* 无动态 */
	SESSION_INIT,		/* 初始化完成 */
	SESSION_DATA,		/* 有新数据到来 */
	SESSION_TIMEOUT,	/* 有数据需要发送 */
	SESSION_BROKEN,		/* 连接断开 */
	SESSION_ERROR		/* 处理会话时发生错误 */
};

/*
 * @brief	当session的缓存连续100次无法写入数据(满)，则状态变为error
 */
#define SESSION_MAX_FULL_TIME	(100)

struct net_session {
	struct list_head list; /* 会话链表的节点 */

	int fd; /* 已连接套接字的描述符 */

	time_t last_alive; /* 最后存活时刻 */
	long long data_send; /* 已经发送的数据量 */
	long long data_recv; /* 已经接收的数据量 */

	//TODO 其他属性
	unsigned int buf_full_count;
	enum session_type type;

	enum session_state state;

	session_proc proc;

	cmfifo_t *read_buffer; /* 从会话接收数据时用来缓存数据 */
	cmfifo_t *write_buffer; /* 通过会话向外发送数据时，用来缓存数据 */

	pthread_mutex_t lock; /* 对会话进行操作时用到的锁 */
};

static inline int lock_session(struct net_session *session)
{
	return (pthread_mutex_lock(&(session->lock)));
}

static inline int trylock_session(struct net_session *session)
{
	return (pthread_mutex_trylock(&(session->lock)));
}

static inline int unlock_session(struct net_session *session)
{
	return (pthread_mutex_unlock(&(session->lock)));
}

/*
 * @brief	构造一个新的会话，使用堆上的空间创建会话结构体以及会话的缓冲FIFO
 * @param	fd: 一个已连接套接字
 * @param	type: 标识该连接是 客户端主动发起的连接，或者服务器收到的连接 TODO 会话本身维护该属性是否必要？
 * @param	rsize,wsize: 指定分别为接收和发送数据创建多大的空间作为缓冲区
 * @param	proc: 指定处理会话事件的回调函数, 定义在net_manager.h中
 * @return	指向新会话的指针,失败则返回NULL
 */
static inline struct net_session* session_new(int fd, enum session_type type,
		size_t rsize, size_t wsize, session_proc proc)
{
	struct net_session * ptr = (struct net_session*) calloc(1,
			sizeof(struct net_session));

	if (ptr == NULL)
		return NULL;

	init_list_head(&(ptr->list));
	ptr->fd = fd;
	ptr->last_alive = time(NULL);

	ptr->type = type;
	ptr->state = SESSION_NONE;
	ptr->proc = proc;

	/* Member 'data_send' & 'data_recv' 'buf_full_count' is already 0 here */

	ptr->read_buffer = cmfifo_create(rsize);
	if (ptr->read_buffer == NULL) {
		free(ptr);
		return NULL;
	}

	ptr->write_buffer = cmfifo_create(wsize); /* NULL (write buffer) is allowed */

	pthread_mutex_init(&(ptr->lock), NULL);
	return ptr;
}

/*
 * @brief	删除会话，以及释放其所占用的所有空间
 * @notice	若会话正处于某会话链表中，则将其移出该链表
 * 		但这里不能保证对该链表安全的并发访问, 而维护该链表的地方应该注意进行同步
 */
static inline void session_delete(struct net_session **s)
{
	if (s == NULL || *s == NULL)
		return;

	release_mutex_t((*s)->lock);

	list_del(&((*s)->list));

	if ((*s)->fd >= 0)
		close((*s)->fd);

	cmfifo_destroy(&((*s)->read_buffer));
	cmfifo_destroy(&((*s)->write_buffer));
	free(*s);
	*s = NULL;
}

/*
 * @brief	关闭一个会话链表，链表head中的所有元素必须保证是struct net_session结构
 * @param	head: 是链表头而不是其中的一个元素
 * @notice	要注意head所对应的链表是否被并发访问,如并发需要对链表加锁
 */
static inline void close_sessions(struct list_head *head)
{
	if (head == NULL)
		return;

	struct list_head *pos = NULL;
	struct list_head *safe;
	list_for_each_safe(pos, safe, head)
	{
		struct net_session *session = list_entry(pos,
				struct net_session, list);
		session_delete(&session);
	}
}

#endif /* NET_SESSION_H_ */
